package com.chuangyiruanke.util;

import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.FileAlreadyExistsException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class FileUtil {

    public static void copyFile(File source, File dest) throws IOException {
        int length = 2097152;
        FileInputStream in = new FileInputStream(source);
        FileOutputStream out = new FileOutputStream(dest);
        FileChannel inC = in.getChannel();
        FileChannel outC = out.getChannel();
        ByteBuffer b = null;
        while (true) {
            if (inC.position() == inC.size()) {
                inC.close();
                outC.close();
                break;
            }
            if ((inC.size() - inC.position()) < length) {
                length = (int) (inC.size() - inC.position());
            } else {
                length = 2097152;
            }
            b = ByteBuffer.allocateDirect(length);
            inC.read(b);
            b.flip();
            outC.write(b);
            outC.force(false);
        }
    }
    /**
     * 递归删除目录下的所有文件及子目录下所有文件
     *
     * @param dir 将要删除的文件目录
     * @return boolean Returns "true" if all deletions were successful.
     * If a deletion fails, the method stops attempting to
     * delete and returns "false".
     */
    public static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            //递归删除目录中的子目录下
            for (int i = 0; i < children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        // 目录此时为空，可以删除
        return dir.delete();
    }

    @NotNull
    public static String getExtension(String fileName) {
        if (StringUtils.isNullOrEmpty(fileName)) {
            return "";
        } else {
            int index = fileName.lastIndexOf(".");
            if (index <= 0 || index == fileName.length() - 1) {
                return "";
            }
            return fileName.substring(index + 1);
        }
    }

    @NotNull
    public static String getFileName(String fullName) {
        if (StringUtils.isNullOrEmpty(fullName)) {
            return "";
        } else {
            int index = fullName.lastIndexOf(File.separator);
            if (index <= 0 || index == fullName.length() - 1) {
                return fullName;
            }
            return fullName.substring(index + 1);
        }
    }

    @NotNull
    public static String getFileNameWithoutExtension(String fileName) {
        if (StringUtils.isNullOrEmpty(fileName)) {
            return "";
        } else {
            int index = fileName.lastIndexOf(File.separator);
            String name = fileName.substring(index + 1);
            index = name.lastIndexOf(".");
            if (index <= 0 || index == fileName.length() - 1) {
                return name;
            }
            return name.substring(0, index);
        }
    }

    @NotNull
    public static String getUniqueName(String ext) {
        UUID uuid = UUID.randomUUID();
        return String.format(Locale.CHINESE, "%s.%s", uuid.toString(), ext);
    }

    @NotNull
    public static String getUniquePath(String parentDir, String ext) {
        UUID uuid = UUID.randomUUID();
        return String.format(Locale.CHINESE, "%s/%s.%s", parentDir, uuid.toString(), ext);
    }

    @Nullable
    public static String getFileMD5(InputStream inputStream) throws IOException {
        // 缓冲区大小
        int bufferSize = 4096;
        DigestInputStream digestInputStream = null;
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            // 使用DigestInputStream
            digestInputStream = new DigestInputStream(inputStream, digest);
            // read的过程中进行MD5处理，直到读完文件
            byte[] buffer = new byte[bufferSize];
            while (digestInputStream.read(buffer) > 0) ;
            // 获取最终的MessageDigest
            digest = digestInputStream.getMessageDigest();
            // 拿到结果，也是字节数组，包含16个元素
            byte[] resultByteArray = digest.digest();
            // 同样，把字节数组转换成字符串
            return StringUtils.toHexString(resultByteArray);
        }
        catch (NoSuchAlgorithmException ex) {
            return null;
        }
    }

    public static String getFileMD5(String inputFile) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(inputFile);
        String result = getFileMD5(fileInputStream);
        fileInputStream.close();
        return result;
    }


    public static void outputFile(File outFile, InputStream inputStream, boolean overwrite) throws IOException  {
        if (outFile.exists()) {
            if (overwrite) {
                outFile.delete();
            } else {
                throw new FileAlreadyExistsException(outFile.getAbsolutePath());
            }
        }
        //输出流
        OutputStream os = new FileOutputStream(outFile);
        byte[] buf = new byte[1024];
        int length = 0;
        while (-1 != (length = inputStream.read(buf))) {
            os.write(buf, 0, length);
        }
        inputStream.close();
        os.close();
    }

    public static void writeZeroToFile(File outFile, long fileSize) throws IOException {
        FileOutputStream stream = new FileOutputStream(outFile);
        int bufferSize = 1024 * 1024;
        byte[] outBuffer = new byte[bufferSize];
        long num = fileSize;
        while (num > 0) {
            int length = bufferSize >= num ? (int) num : bufferSize;
            stream.write(outBuffer, 0, length);
            num -= bufferSize;
        }
    }

    public static void writeBufferToFile(File outFile, long position, byte[] buffer)
            throws FileNotFoundException, IOException {
        RandomAccessFile randomAccessFile = new RandomAccessFile(outFile, "rw");
        randomAccessFile.seek(position);
        randomAccessFile.write(buffer);
    }

    /**
     * 压缩指定文件夹中的文件
     *
     * @param dir             需打包的文件夹名称
     * @param targetFileName  压缩后的文件名称
     * @param containDir      指示是否包含 dir 文件夹
     */
    public static void writeZipFile(String  dir, String targetFileName, boolean containDir) throws IOException {
        ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(targetFileName));
        if (containDir) {
            writeZip(new File(dir), "", zipOutputStream);
        } else {
            File file = new File(dir);
            if (!file.exists()) {
                throw new FileNotFoundException("无法创建压缩文件");
            }
            File[] files = file.listFiles();
            if (files == null) {
                return;
            }
            for (File childFile : files) {
                writeZip(childFile, "", zipOutputStream);
            }
        }
        zipOutputStream.close();
    }

    /**
     * 压缩指定的多个文件 （不保存目录结构）
     *
     * @param targetFileName     压缩后的文件名称
     * @param srcFiles 需压缩的文件列表
     */
    public static void writeZipFile(String targetFileName, String... srcFiles) throws IOException {
        byte[] buffer = new byte[1024];
        //生成的ZIP文件名为Demo.zip
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(targetFileName));
        //需要同时下载的两个文件result.txt ，source.txt
        for (String fileName : srcFiles) {
            File file = new File(fileName);
            FileInputStream fis = new FileInputStream(file);
            out.putNextEntry(new ZipEntry(file.getName()));
            //读入需要下载的文件的内容，打包到zip文件
            int len;
            while ((len = fis.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
            fis.close();
        }
        out.closeEntry();
        out.close();
    }

    public static void writeZip(File file, String parentPath, ZipOutputStream zos) {
        if (file.exists()) {
            if (file.isDirectory()) {//处理文件夹
                parentPath += file.getName() + "/";
                File[] files = file.listFiles();
                if (files.length != 0) {
                    for (File f : files) {
                        writeZip(f, parentPath, zos);
                    }
                } else {       //空目录则创建当前目录
                    try {
                        zos.putNextEntry(new ZipEntry(parentPath));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } else {
                FileInputStream fis = null;
                try {
                    fis = new FileInputStream(file);
                    ZipEntry ze = new ZipEntry(parentPath + file.getName());
                    zos.putNextEntry(ze);
                    byte[] content = new byte[1024];
                    int len;
                    while ((len = fis.read(content)) != -1) {
                        zos.write(content, 0, len);
                        zos.flush();
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (fis != null) {
                            fis.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static void writeToStream(InputStream inputStream, OutputStream outputStream) throws IOException {
        byte[] buf = new byte[1024];
        int length = 0;
        while (-1 != (length = inputStream.read(buf))) {
            outputStream.write(buf, 0, length);
        }
        inputStream.close();
        outputStream.close();
    }
}
